package de.lmu.ifi.pst.uwejsf.framework.process;

import java.util.HashMap;
import java.util.Map;

import javax.faces.model.DataModel;

import de.lmu.ifi.pst.uwejsf.framework.AbstractUWENavigator;
import de.lmu.ifi.pst.uwejsf.framework.ViewInfo;

public abstract class ProcessActivityBean {
	protected InitialNode initialNode;

	protected ActivityNode activeNode;

	protected ActivityGuardHandler guardHandler;

	protected Map<String, Object> objectStorage = new HashMap<String, Object>();

	protected Map<String, Action> actionMap = new HashMap<String, Action>();

	protected ViewInfo activeViewBeforeStart;

	protected Object inputObject;

	protected String outcome;

	protected ProcessActivityBean callingActivity;

	public Map<String, Action> getActionMap() {
		return actionMap;
	}

	public void setActionMap(Map<String, Action> actionMap) {
		this.actionMap = actionMap;
	}

	public Map<String, Object> getObjectStorage() {
		return objectStorage;
	}

	public void setObjectStorage(Map<String, Object> objectStorage) {
		this.objectStorage = objectStorage;
	}

	public void setInitialNode(InitialNode initiaNode) {
		this.initialNode = initiaNode;
	}

	public InitialNode getInitialNode() {
		return initialNode;
	}

	public ActivityNode getActiveNode() {
		return activeNode;
	}

	public void setActiveNode(ActivityNode activeNode) {
		this.activeNode = activeNode;
	}

	public abstract String getName();

	public abstract MainProcessClassBean getMainProcessClassBean();

	protected String process() {
		return process(true);
	}

	protected String process(boolean navigate) {
		boolean proceed = true;
		while (proceed) {
			if (activeNode == null) {
				proceed = false;
			} else if (activeNode instanceof FinalNode) {
				FinalNode fn = (FinalNode) activeNode;
				if (fn.getName() != null && fn.getName().trim().length() > 0)
					this.outcome = fn.getName().trim();
				else
					outcome = null;
				proceed = false;
			} else if (activeNode instanceof UserAction) {
				UserAction ua = (UserAction) activeNode;
				if (navigate)
					return AbstractUWENavigator.getNavigator()
							.chooseTargetView(ua);
				else
					return null;
			} else if (activeNode instanceof SystemAction) {
				SystemAction sa = (SystemAction) activeNode;
				sa.execute();
				setActiveNode(sa.getFollower());
			} else if (activeNode instanceof CallProcessAction) {
				CallProcessAction cpa = (CallProcessAction) activeNode;
				return cpa.execute();
			} else if (activeNode instanceof CallOperationAction) {
				CallOperationAction coa = (CallOperationAction) activeNode;
				coa.execute();
				setActiveNode(coa.getFollower());
			} else {
				setActiveNode(activeNode.getFollower());
			}

		}
		cleanup();
		// If we arrive here, no user action has been found so we must follow
		// an outgoing link of the main process class or return to the activity
		// that has called this activity.

		if (callingActivity != null)
			return callingActivity.handleCalledActivityCompleted(this);
		else {
			if (navigate)
				return AbstractUWENavigator.getNavigator()
						.handleOutgoingNavigation(getMainProcessClassBean());
		}
		return null;
	}

	protected void cleanup() {
		inputObject = null;
		objectStorage.clear();
		activeNode = null;
		for (Action action : actionMap.values()) {
			if (action instanceof UserAction) {
				ProcessClassBean pcb = ((UserAction)action).getProcessClassBean();
				pcb.clearProperties();
			}
		}
	}

	public String handleSubmit(String userActionName) {

		if (!(activeNode instanceof UserAction))
			throw new IllegalProcessStateException(
					"Submit when active node is no UserAction!");
		UserAction ua = (UserAction) activeNode;
		if (!ua.getName().equals(userActionName))
			throw new IllegalProcessStateException(
					"Wrong UserAction: expected " + ua.getName() + ", got: "
							+ userActionName);

		ua.storeSubmittedValues();
		setActiveNode(ua.getFollower());

		return process();
	}

	public void runToUserAction(String userActionName, ViewInfo currentView) {
		this.callingActivity = null;
		activeViewBeforeStart = currentView;
		setActiveNode(getInitialNode());
		applyInputParameter();
		process(false);
		if (!(activeNode instanceof UserAction)
				|| !((UserAction) activeNode).getName().equals(userActionName)) {
			throw new IllegalProcessStateException(
					"Couldn't follow control flow in activity " + getName()
							+ " to user action " + userActionName + ".");

		}
	}

	public String handleCalledActivityCompleted(
			ProcessActivityBean returningActivity) {
		if (!(activeNode instanceof CallProcessAction))
			throw new IllegalProcessStateException(
					"Received return signal from Activity though active node is not a CallProcessAction!");
		CallProcessAction cpa = (CallProcessAction) activeNode;
		if (cpa.getCalledActivity() != returningActivity)
			throw new IllegalProcessStateException(
					"Received return signal from wrong activity!");
		Map<String, Object> resultParams = new HashMap<String, Object>();
		resultParams.put("result", returningActivity.getResult());
		resultParams.put("outcome", returningActivity.getOutcome());
		cpa.storeResultValues(resultParams);
		setActiveNode(cpa.getFollower());
		return process();
	}

	public String start(ViewInfo currentView) {
		return start(currentView, null);
	}

	public String start(ViewInfo currentView,
			ProcessActivityBean callingActivity) {
		this.callingActivity = callingActivity;
		activeViewBeforeStart = currentView;
		setActiveNode(getInitialNode());
		applyInputParameter();
		return process();
	}

	

	public abstract void applyInputParameter();

	protected Object result;

	public Object getResult() {
		return result;
	}

	public void setOutputParameter(Object obj) {
		result = obj;

	}

	public void storeObject(String name, Object value) {
		objectStorage.put(name, value);
	}

	public void addAction(String name, Action action) {
		actionMap.put(name, action);
	}

	public void fillActionProperties(String actionName, Object bean) {
		Action action = actionMap.get(actionName);
		if (action == null)
			throw new ProcessDataflowException("Action " + actionName
					+ " not found in activity " + getName() + ".");

		action.populateInputValues(bean);
	}

	public void setActionInputPin(String actionName, String propertyName,
			Object value) {
		if (value instanceof DataModel) {
			value = ((DataModel) value).getWrappedData();
		}
		Action action = actionMap.get(actionName);
		if (action == null)
			throw new ProcessDataflowException("Action " + actionName
					+ " not found in activity " + getName() + ".");
		action.setInputValue(propertyName, value);

	}

	public ActivityGuardHandler getGuardHandler() {
		return guardHandler;
	}

	public void setGuardHandler(ActivityGuardHandler guardHandler) {
		this.guardHandler = guardHandler;
	}

	public ViewInfo getActiveViewBeforeStart() {
		return activeViewBeforeStart;
	}

	public void setActiveViewBeforeStart(ViewInfo activeViewBeforeStart) {
		this.activeViewBeforeStart = activeViewBeforeStart;
	}

	public Object getInputParameter() {
		if (callingActivity == null) {
			return getMainProcessClassBean().getUWEInput();
		} else
			return inputObject;
	}

	public void setInputParameter(Object param) {
		this.inputObject = param;
	}

	public ProcessActivityBean getCallingActivity() {
		return callingActivity;
	}

	public void setCallingActivity(ProcessActivityBean callingActivity) {
		this.callingActivity = callingActivity;
	}

	public String getOutcome() {
		return outcome;
	}

	public void setOutcome(String outcome) {
		this.outcome = outcome;
	}
}
